import os
import sys
import yaml
import decimal
import traceback
import logging
import logging.handlers as handlers
import pymysql

from flask import json
from flask import Flask, Response, jsonify, logging as flask_logging
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from app.common.Interceptor import Interceptor
from app.model.base.RestResponse import RestResponse

# lp-backtest/
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_PATH)

db = SQLAlchemy()


def create_app(config_path=f'{BASE_PATH}/config.yaml'):
    # 初始化 app, 读取config
    app = Flask(__name__)
    app.config.update(read_config(config_path))

    # 设置json返回值 处理
    app.json_encoder = JsonEncoder
    app.response_class = JsonResponse

    # 配置日志
    configration_logger(app)

    # 注册db配置
    register_database(app)

    # 注册拦截器
    # register_interceptor(app)

    # 注册蓝图
    register_blueprint(app)

    return app


def configration_logger(app):
    # # 配置日志
    LOG_PATH = app.config['LOG_PATH']
    formatter = logging.Formatter(
        '%(asctime)s %(thread)s %(levelname)s %(filename)s.%(lineno)d - %(funcName)s :: %(message)s')

    # 日志基础目录
    if not os.path.exists(LOG_PATH):
        os.mkdir(LOG_PATH)

    info_rh = handlers.TimedRotatingFileHandler(
        f'{LOG_PATH}/info.log', when='D', backupCount=15)
    info_rh.setLevel(logging.DEBUG)
    info_rh.setFormatter(formatter)
    info_rh.addFilter(InfoFilter())

    error_rh = handlers.TimedRotatingFileHandler(
        f'{LOG_PATH}/error.log', when='D', backupCount=15)
    error_rh.setLevel(logging.ERROR)
    error_rh.setFormatter(formatter)

    # 增加文件处理
    app.logger.level = logging.DEBUG
    app.logger.addHandler(info_rh)
    app.logger.addHandler(error_rh)
    # flask 默认打印格式
    flask_logging.default_handler.setFormatter(formatter)

    # 日志 全局异常拦截
    def _excepthook(etype, evalue, tb):
        traces = traceback.format_exception(etype, evalue, tb)
        app.logger.error(''.join(traces).strip())

    sys.excepthook = _excepthook


def register_database(app):
    db_config = app.config['DATASOURCES']
    conn_temp = 'mysql+pymysql://{USER}:{PASS}@{HOST}:{PORT}/{DB}?charset=utf8'

    # 配置多数据源
    binds = {}
    for db_key in db_config.keys():
        binds[db_key] = conn_temp.format(**db_config[db_key])

    app.config['SQLALCHEMY_DATABASE_URI'] = binds['MASTER']
    app.config['SQLALCHEMY_BINDS'] = binds
    db.init_app(app)


def register_blueprint(app):
    from app.controller import TestController

    app.register_blueprint(TestController.web)


def register_interceptor(app):
    Interceptor.register(app)


def read_config(config_path):
    with open(config_path, 'r') as f:
        cf = yaml.safe_load(f)
    return cf


class InfoFilter(logging.Filter):
    def filter(self, record):
        if logging.DEBUG <= record.levelno <= logging.WARN:
            return 1
        else:
            return 0


class JsonResponse(Response):
    @classmethod
    def force_type(cls, resp, environ=None):
        if isinstance(resp, (list, dict)):
            response = jsonify(resp)
        if isinstance(resp, RestResponse):
            response = jsonify(resp.response)
        return super().force_type(response, environ)


class JsonEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return '{0:.4f}'.format(o)
        return json.JSONEncoder.default(self, o)
